#!/usr/bin/perl
###################################
#
#       Name: QnD Simple Bot
#
#       
#
#       Code:  This code was meant to be easily read to target the new and seasoned Perl programmers
#               Feel free to modify at will,  but please leave the Name and Developer comment in tact
#
###################################

####    ******** IMPORTANT *********
####    Remove ALL LB and user specific code and VAR values;

# We will use a raw socket to connect to the IRC server.
use IO::Socket;
use LWP::Simple;######FOR
use HTTP::Cookies;#######^User Grab seanachi API
use feature qw/switch/;
use Time::Piece;
use Time::Seconds;
use JSON;

my $botFilename = $0;
my $starttime = localtime->epoch;
my $version = "Bot code Beta v2";
my $issuer ='';
my @response;
my $responded="new";
my @cmdLog;

# connecton VARS
my $server = "irc.";
my $nick = "";
my $adminName = ""; #hardcoded admin
my @admins;
my $login = "";
my $pass = "";
#my $channel = "";
my $channel = "";
my $joined = "false";


&loadHelp; #reads command list in to an array
my $sock = new IO::Socket::INET(PeerAddr => $server,
                                PeerPort => 6667,
                                Proto => 'tcp') or
                                    die "Can't connect\n";

#TODO:  clean up inconsistancies and code,  error handeling needed too   REFACTOR HERE !
# Log on to the server.
# MAIN()
print "\n======================= STARTING: $botFilename ============================\n";
print $sock "NICK $nick\r\n";
print $sock "USER $login 8 * :BrokenBot\r\n";
print $sock "PASS $pass\r\n";

# Keep reading lines from the IRC server.
#this handles connection to room
#
#TODO: CLEAN UP
#
while (my $input = <$sock>) {
        print "S-$input\n";
    chop $input;
    if ($input =~ /^PING(.*)$/i) {
        # We must respond to PINGs to avoid being disconnected.
        print $sock "PONG $1\r\n";
    }
    if ($input =~ /Password accepted/) {  #wait for auth and banner to be done to join room
        print $sock "JOIN  $channel\r\n";
        #send($sock,"PRIVMSG $channel :I am $nick version: $version\r\n",0);
        }
    # joined and recognized grab user list for perms
    if($input =~ /you are now recognized/){$runonce = 1;}
    if ($runonce){
      push(@response,$input);
      if ($input =~ /End/){  ######user strings grabbed in @response
        $responded = "done";
        &permUsers();  ####### set initial user - admin relation.
        &mainParse; #turn over connected bot to its handeler
      }
    }
} #end while


############### MAIN PARSE
#
sub mainParse{
  while (my $in = <$sock>){ #main socket stream  Needs reconnect on disconnected routine
        &pingPong($in);
        print "Raw- $in\r\n";
        ############can regex split handeler and automated commands HERE
                if ($in =~ /^:(.+)!.+PRIVMSG\s(.*)\s:$nick/){&botResponse($in);}   #calls &botResponse when bots nick is command
        if ($in =~ /^:(.+)!.+PRIVMSG\s(.*)\s:(![a-z]+)\s(.*)/) {
          my $issuer = $1;
          my $dest = $2;
          my $comm = $3;
          my $ar1 = $4;
          &autoActions($in); #Bots sutomatic subs
          $responded=$comm;
          @response=();
          if ($dest eq $nick){$dest=$issuer;} ###flip the script for PM

          print"issuer = $issuer \r\n";
          print"dest = $dest \r\n";  # $dest in pm will become bots name causing bot to msg bot error
          print"COMM = $comm \r\n";
          print"ar1 = $ar1 \r\n";

        ############ TODO: add !fixadm password  run opMode on start  create a runOnce container
                #Admin runs ADMIN COMMANDS and User runs USER COMMANDS both seperate OPTIONAL: allow Admin to run User commands
				#the #"U" notation in the comments after the commands are to add usage notes to the help listing
          if (/$issuer/ ~~ @admins){ ######ADMIN HANDELER
            given($comm) {
              when(/^!u$/){&uShow($issuer)} #U#UE #display self stats
              when(/^!uexist$/){&$userExist($ar1,$issuer)} #U<User Name>#UE
              when(/^!udetail$/){&userDetail($ar1,$issuer)} #U<User Name>#UE
              when(/^!userip$/){&userIp($channel,$ar1)} #U<User Name>#UE #displays to channel  CHANGE
              when(/^!up$/){&upTime;}#U#UE
              when(/^!list$/){&modeList($dest);}   #useless for now
              when(/^!version$/){&say($channel,"I am $nick: $version");print "NICK MATCHED\n";}#U#UE
              when(/^!help$/){&help($issuer)}#U#UE
              when(/^!which$/){&say($channel,"FILE: $botFilename \n");}#U#UE
              when(/^!op$/){&opMode($dest);}#U#UE
              when(/^!botjoin$/){print $sock "JOIN  $ar1\r\n";}#U<#channel>#UE
              when(/^!botpart$/){print $sock "PART  $ar1\r\n";}#U<#channel>#UE
              when(/^!bye$/){print $sock "QUIT  $ar1\r\n";}#U#UE

              when(/^!lock$/){@admins=("$adminName");&displayAdmins}#U#UE
              when(/^!test$/){&say($dest,"$issuer: Admin");}#U#UE #now works from chan and PM
              when(/^!admins$/){&displayAdmins}#U#UE
              when(/^!deladm$/){&delAdmin($ar1)}#U<User Name>#UE
              when(/^!addadm$/){&addAdmin($ar1)}#U<User Name>#UE
              when(/^!popadm$/){&fixAdmin($dest)}#U#UE  ######for when I lock my dumb ass out :)
            }#ADMIN END
          }else{##########USER HANDLER
             given($comm) {
              when(/^!test$/){say($channel,"$issuer: NOT Admin");}
              when(/^!resetadm$/){&fixAdmin}  ######for when I lock my dumb ass out :) TODO: ADD PASSWORD
              when(/^!u$/){&uShow($issuer)} #display self stats
            }#USER END
          }#end else
       }#end regex
  }#end sock in
}#end mainParse()


########Bot response
sub botResponse{
  my $in=$_[0];
    if ($in =~ /^:(.+)!.+PRIVMSG\s(.*)\s:(\w+)\s(\w+)\s(.*)/) {
      my $issuer = $1;
      my $dest = $2;
      my $bottt = $3;
      my $comm = $4;
      my $arg1 = $5;
      my $toSay = "$arg1, ";
      $exe .= `wget randominsults.net -O - 2>/dev/null | grep "<i>"`;
      $exe =~ /<i>(.*)<\/i>.*/;
      $exe = $1;
      $arg1 =~ s/\s+$//;        #gets rid of any garbage at the end of the string
      $toSay = $arg1 . ", " . $exe;
      print "\nBR- arg1: $arg1\n";
      given($comm) {
              when(/^insult$/){&say($channel,$toSay)}
              when(/^btc$/){$btc= decode_json(`wget https://www.bitstamp.net/api/ticker/ -O - 2>/dev/null`);
                &say($channel,$btc->{$arg1})}
      }
      print "toSay: $toSay \n";
    }
  print "BotResponse: $in \n";
}

########Automatic actions
sub autoActions{
  my $in=$_[0];
  if ($in =~ /^:(.+)!.+PRIVMSG\s(.*)\s:(![a-z]+)\s(.*)/) {&logger($in);}
}



######### bot subroutines HERE

##########Logger TODO:  write log to file if(Log:boolean)
sub logger{
  my $in=$_[0];
  my $entry=localtime;
  $entry .= "-$in";
  print "\n$entry ***LOGGED***\n";
  push(@cmdLog,$entry);
}

sub upTime
{
  $t = localtime->datetime;
  $ep = localtime;
  $x = localtime->epoch - $starttime;
  my $tval = Time::Seconds->new($x);
  $prettytime = $tval->pretty;
  #&say($channel,"\0038,04 UP TIME: $prettytime");#Yellow on Red BK
  &say($channel,"\0038,04 UP TIME: $prettytime");
}

sub userIp{
  my $disp=$_[0];
  my $user=$_[1];
  print $sock "USERIP $user\r\n";
  $ip=&getResponse("340","");
  if ($ip =~/^\s.*/){$ip="NOT FOUND";}
  &say($disp,$ip);

}

sub displayAdmins{
  $admStr = "Admins: " . join(",",@admins);
  print "$admStr\n";
  &say($channel,$admStr);
}

sub delAdmin{    ####just as fast as split / join method
  my $admn=$_[0];
  my @tadmins=@admins;
  @admins=();
  $admn =~ s/\s+$//; #strips damn whitespace
  foreach(@tadmins){
    if ($_ =~ m/$admn/ ){print"FOUND\n";}else{push(@admins,$_);}
  }
  &displayAdmins;
}

sub addAdmin{
  my $admn=$_[0];
  $admn =~ s/\s+$//;  #gets rid of any garbage at the end of the string
  push(@admins,$admn);
  &displayAdmins;
}

sub fixAdmin{
  my $currentRoom = $_[0];
  &modeList($currentRoom);
  &permUsers;
}

sub modeList{   ### populates @response with /NAMESA
        my $currentRoom = $_[0];
        my $comStr = "NAMES $currentRoom";
        #print $sock "NAMES #Area51\r\n"; #old line NAMES #Area51
        print $sock "$comStr\r\n";
        #say($currentRoom, $comStr);
        &getResponse("353","366");#353 NAMES reply &  #366 NAMES end
        print join("\n",@response),"\n";#print array of users to console
        print "Responded = $responded\r\n";
        say("panhead1956", "Admins: " . join(",",@admins));
}

#####populate @admins
sub permUsers{
        my @usersRaw;
        #@usersRaw = grep /.*@.*:.*/,@response;
        @usersRaw = grep /:.*353.*/,@response; #grep for #353 - NAMES reply
        #@usersRaw = grep /:.*:eduTron.*/,@response;
        $line = @usersRaw[0] =~ s/:.*://r;
        #print "\r\n-----LINE: $line \r\n";
        my @abc = split(' ',$line);
        @admins = grep /[@~&]/,@abc;  #add user as admin if [~@&]
        print "\nAll users :\n";
        print join("\n",@abc),"\n";
        print "\nAdmins:\n";
        for(@admins){s/[@~&]//g};
        print join("\n",@admins),"\n";
        print "\n_______DONE________\n";
        #return @admins; #####################returns BOT admins based on [~@&]
}











############ !help prints command list
sub help{
  my $disp=$_[0];
  my $adminStr;
  my $userStr;
  print "**********HELP\n";
  foreach(@cmdList){
    my $ln=$_;
    if ($ln =~ /admin.*(!.*)/){$adminStr .="$1 "}
    if ($ln =~ /user.*(!.*)/){$userStr .="$1 "}
  }
    &say($disp,"Commands\n");
    &say($disp,"Admin: $adminStr\n");
    &say($disp,"User: $userStr\n");
}

############Form Command List by reading running bots filename $0
sub loadHelp{
  ########################grab code into var
  my @code = do {
    open my $fh, "<", $botFilename
    or die "could not open $botFilename: $!";
    <$fh>;
  };
  foreach(@code){
    $ln = $_;
    if ($ln =~ /#ADMIN HANDELER/){$fl="admin"}
    if ($ln =~ /#USER HANDLER/){$fl="user"}
    if ($ln =~ /#USER END/){$fl="done"}
    given($fl) {
      #when(/admin/){if ($ln =~ /.*(!\w+).*/){push (@cmdList,"$fl $1");}}
      when(/admin/){if ($ln =~ /.*(!\w+).*#U(.*)#UE.*/){push (@cmdList,"$fl $1$2");}}
      when(/user/){if ($ln =~ /.*(!\w+).*/){push (@cmdList,"$fl $1");}}
    }
  }
  foreach(@cmdList){
    print"$_\n";
  }
}

########se bot to operator
sub opMode{
        my $disp=$_[0];
        my $opPass="jj12211987";     #############COMMENT OUT on code share
        print "OP mode set in progress\n";
        print $sock "OPER $nick $opPass\r\n";
        &say($channel,&getResponse("008",""));
        #&say($channel,$response[0]);
        print "OP mode- $response[0]\n";
}

########user check
sub userExist{
  my $usr=$_[0];
  my $disp=$_[1];
  my @result = &userInfo($usr);
    given($result[0]) {
      when(/not found/i){&say($disp,"Not found: $usr");}
      when(/^id/i){&say($disp,$result[6]=~s/:/=/r . ": $usr");}
    }
}

########user detail
sub userDetail{
  my $usr=$_[0];
  my $disp=$_[1];
  my @result = &userInfo($usr);
  if ($result[0] =~/^id/i){
    #say($disp,$usr);
    foreach(@result)
      {
        &say($disp,$_);
      }
  }else{&say($disp,"Error $user not found")}

}

#########user show
################## outputs -> username: | last_access: | uploaded: | downloaded: | ratio:
sub uShow{
  my $usr=$_[0];
  my $showStr;
  my @result = &userInfo($usr);
  if ($result[0] =~ /id/){
    for ($i = 1; $i <= 4; $i++) {
      $showStr .=  "$result[$i] | ";
    }
    $showStr .= $result[9];
    &say($channel,$showStr);
  }
}

#Bots mouth
sub say   ###&sub(<towhere>,<text>)
{
  $towhere = $_[0];   #channel or nick
  $txt = $_[1];  #say what ?
  chomp($txt);
  send($sock, "PRIVMSG $towhere :$txt\r\n",0);
}

##############get response from command with END commandNum
###########################################################  REFACTOR NEEDED !  just Quick n Dirty
sub getResponse {
  my $i = 0;
  my $eventCode=$_[0]; #353 NAMES reply
  my $eventEnd=$_[1];  #366 NAMES end
  print "getResponse()\r\n";
  while (my $in = <$sock>){ #main socket stream
    print "R-$in\r\n";
    &pingPong($in);  #keep alive - dont miss "ping" in $sock data

    if ($eventEnd eq ""){ #single numericCode response  Dirty code
        if ($in =~ /^:$server\s(\d\d\d)/){
          push(@response,$in);
          print "Single- $in\n";
          #if ($1 eq $eventCode){return "OK";}else{return "FAIL";}
          if ($1 eq $eventCode){
            $in=~/:.*:(.*)/;
            print "\n------------------SingleReturn:$1 \n";
            return $1;}else{return "FAIL"}              #returns command response part of string on single event code
        }
    }

    if ($in =~ /^:$server\s(\d\d\d)/){ #### IRC server message number
      given($1) {
        when(/$eventCode/){push(@response,$in);print"\nPushed #$eventCode - $in\n";}
        when(/$eventEnd/){$responded = "done";print"\n#$eventEnd - Event END\n";return "OK";}
      }
    }
    $i++;
    if($i > 10){$responded="Error i > 10";return;} #runaway limiter - failsafe REVISIT needs
  }
}


###IRC ping pong keepalive
sub pingPong{
  my $input =$_[0];
  chop $input;
  if ($input =~ /^PING(.*)$/i) {
    # We must respond to PINGs to avoid being disconnected.
    print $sock "PONG $1\r\n";
    print "pingPong heartbeat\n";  #####for debugging purposes
  }
}

######User Lookup via Seanachi API
#Grab user info
sub userInfo{
        my $userName = $_[0];
        my $ua = LWP::UserAgent->new();
        push @{ $ua->requests_redirectable }, 'POST';
        my $cookies = new HTTP::Cookies(file=>'~/bot/cookies.txt',autosave=>0, ignore_discard=>1);
        $ua->cookie_jar($cookies);
        my $req = HTTP::Request->new(POST => "http://www.learnbits.me/bot_api.php?c=user&name=$userName");
        my $res = $ua->request($req);
        my $rawUser = $res->decoded_content;
        $rawUser =~ s/"//g;
        if ($rawUser =~ /error/){return @err=('User not found');}
        $rawUser =~ m/\{(.*)\}/;
        my $data = $1;
        my @userDetail =split(/[,]/, $data,);
        splice @userDetail, 7 , 1; #ditch class:nn
        #print "\nUser Full:\n";
        #foreach(@userDetail)
        #{
        #  print "$_\r\n";
        #}
        return @userDetail;
}